package goxele

/*
#cgo CXXFLAGS: -std=C++11
#cgo linux LDFLAGS: -lpthread -lrt -L${SRCDIR}/lib -Wl,-rpath=${SRCDIR}/lib -l trade

void* createApi();
const void *getVersion();
const void *getErrorMessage(int errorCode);
void* createSpi();
int setConfig(void* api, const char* name, const char* value);

int start(void *api, void *spi);
int stop(void *api);
int login(void *api);
int logout(void *api);
int changePassword(void *api, char *password, char *newPassword);
int insertOrder(void *api, void *instrumentID, void *inputOrder);
int cancelOrder(void *api, long orderID);
void *getInstrumentByID(void *api, char *instrumentID);
int findOrders(void *api);
int subscribe(void *api, char *instrumentID);
int unsubscribe(void *api, char *instrumentID);
int updateBook(void *api, char *instrumentID, double lastPrice, double bidPrice, int bidVolume, double askPrice, int askVolume);

void SetOnStart(void *spi, void *onFunc);
void SetOnStop(void *spi, void *onFunc);
void SetOnLogin(void *spi, void *onFunc);
void SetOnLogout(void *spi, void *onFunc);
void SetOnChangePassword(void *spi, void *onFunc);
void SetOnReadyForTrading(void *spi, void *onFunc);
void SetOnLoadFinished(void *spi, void *onFunc);
void SetOnOrder(void *spi, void *onFunc);
void SetOnCancelOrder(void *spi, void *onFunc);
void SetOnTrade(void *spi, void *onFunc);
void SetOnAccount(void *spi, void *onFunc);
void SetOnExchange(void *spi, void *onFunc);
void SetOnInstrument(void *spi, void *onFunc);
void SetOnBookUpdate(void *spi, void *onFunc);
void SetOnEvent(void *spi, void *onFunc);
void SetOnError(void *spi, void *onFunc);

void exOnStart(int errorCode, _Bool isFirstTime);
void exOnStop(int errorCode);
void exOnLogin(int errorCode, int exchangeCount);
void exOnLogout(int errorCode);
void exOnChangePassword(int errorCode);
void exOnReadyForTrading(void *account);
void exOnLoadFinished(void *account);
void exOnOrder(int errorCode, void *order);
void exOnCancelOrder(int errorCode, void *order);
void exOnTrade(void *trade);
void exOnAccount(int event, int action, void *account);
void exOnExchange(int event, int action, void *exchange);
void exOnInstrument(int event, void *instrument);
void exOnBookUpdate(void *book);
void exOnEvent(void* event);
void exOnError(int errorCode, void* data, size_t size);

*/
import "C"
import (
	"fmt"
	"unsafe"

	"golang.org/x/text/encoding/simplifiedchinese"
)

var t *Trade

type Trade struct {
	api, spi    unsafe.Pointer
	Instruments map[string]unsafe.Pointer

	OnStart           func(errorCode int, isFirstTime bool)
	OnStop            func(errorCode int)
	OnLogin           func(errorCode int, exchangeCount int)
	OnLogout          func(errorCode int)
	OnChangePassword  func(errorCode int)
	OnReadyForTrading func(account *XTFAccount)
	OnLoadFinished    func(account *XTFAccount)
	OnOrder           func(errorCode int, order *XTFOrder)
	OnCancelOrder     func(errorCode int, cancelOrder *XTFOrder)
	OnTrade           func(trade *XTFTrade)
	// 账户信息发生变化时回调该接口，如：出入金变化
	OnAccount func(event int, action int, account *XTFAccount)
	// 交易所信息发生变化时回调该接口，如：交易所前置变化
	OnExchange func(event int, action int, exchange *XTFExchange)
	// 合约属性发生变化时回调该接口，如：状态变化
	OnInstrument func(event int, instrument *XTFInstrument)
	OnBookUpdate func(book *XTFMarketData)
	OnEvent      func(event *XTFEvent)
	OnError      func(errorCode int)
}

func NewTrade() *Trade {
	ver := C.GoString((*C.char)(C.getVersion()))
	fmt.Println("API 版本: ", ver)

	t = &Trade{}
	t.Instruments = make(map[string]unsafe.Pointer)

	t.api = C.createApi()
	t.spi = C.createSpi()

	t.SetOnLoadFinished()
	// t.SetOnReadyForTrading()
	t.SetOnStart()
	t.SetOnStop()

	t.SetOnLogin()
	t.SetOnLogout()

	t.SetOnChangePassword()
	t.SetOnExchange()
	t.SetOnInstrument()
	t.SetOnAccount()

	t.SetOrder()
	t.SetOnCancelOrder()
	t.SetOnTrade()

	t.SetOnError()
	t.SetOnEvent()

	t.SetOnBookUpdate()
	return t
}
func (t *Trade) GetErrorMessage(errorCode int) string {
	str := C.GoString((*C.char)(C.getErrorMessage(C.int(errorCode))))
	str, _ = simplifiedchinese.GB18030.NewDecoder().String(str)
	return str
}

/**
 * 设置会话参数
 *
 * 应在启动会话之前设置参数，启动之后设置的参数不会生效。
 *
 * 会话参数名称列表：
 * "ACCOUNT_ID"：资金账户ID
 * "ACCOUNT_PWD"：资金账户密码
 * "APP_ID"：应用程序ID
 * "AUTH_CODE"：认证授权码
 * "TRADE_SERVER_IP"：交易服务地址
 * "TRADE_SERVER_PORT"：交易服务端口
 * "QUERY_SERVER_IP"：查询服务地址
 * "QUERY_SERVER_PORT"：查询服务端口
 * "HEARTBEAT_INTERVAL"：心跳间隔时间，单位：毫秒
 * "HEARTBEAT_TIMEOUT"：心跳超时时间，单位：毫秒
 * "TCP_RECONNECT_ENABLED"：TCP断开后是否重连
 * "TCP_RETRY_INTERVAL"：TCP重连最小间隔时间，单位：毫秒
 * "TCP_RETRY_INTERVAL_MAX"：TCP重连最大间隔时间，单位：毫秒
 * "TCP_RETRY_COUNT"：TCP重连次数
 * "TCP_WORKER_CORE_ID"：数据收发线程绑核
 * "TCP_WORKER_BUSY_LOOP_ENABLED"：数据收发线程是否启用BusyLoop模式运行
 * "TRADE_WORKER_CORE_ID"：报单处理线程绑核
 * "TASK_WORKER_CORE_ID"：通用任务线程绑核
 * "POSITION_CALC_ENABLED"：是否计算仓位
 * "MONEY_CALC_ENABLED"：是否计算资金，如果启用资金计算，会默认启用仓位计算
 * "HISTORY_ONLY_CALC_ENABLED"：是否仅计算历史流水的资金和仓位，如果启用，那么历史流水追平后，将不再计算资金和仓位
 * "WARM_INTERVAL"：预热时间间隔，取值范围：[10,50]，单位：毫秒
 * "ORDER_MT_ENABLED"：是否启用多线程报单功能
 *
 * 用户也可以设置自己的参数，以便在需要的地方使用 getConfig() 接口进行获取。
 * 会话内部不使用这部分用户参数，仅对其临时存储。
 *
 * @param name 参数名称
 * @param value 参数值
 * @return 参数配置成功或失败
 */
func (t *Trade) SetConfig(name, value string) int {
	return int(C.setConfig(t.api, C.CString(name), C.CString(value)))
}

func (t *Trade) Start() int {
	return int(C.start(t.api, t.spi))
}
func (t *Trade) Stop() int {
	return int(C.stop(t.api))
}
func (t *Trade) Login() int {
	return int(C.login(t.api))
}
func (t *Trade) Logout() int {
	return int(C.logout(t.api))
}
func (t *Trade) GetInstrumentByID(instrumentID string) unsafe.Pointer {
	return C.getInstrumentByID(t.api, C.CString(instrumentID))
}
func (t *Trade) ChangePassword(password, newPassword string) int {
	return int(C.changePassword(t.api, C.CString(password), C.CString(newPassword)))
}

func (t *Trade) Subscribe(instrumentID string) int {
	return int(C.subscribe(t.api, C.CString(instrumentID)))
}
func (t *Trade) UnSubscribe(instrumentID string) int {
	return int(C.unsubscribe(t.api, C.CString(instrumentID)))
}
func (t *Trade) UpdateBook(instrumentID string, lastPrice float64, bidPrice float64, bidVolume int, askPrice float64, askVolume int) int {
	return int(C.updateBook(t.api, C.CString(instrumentID), C.double(lastPrice), C.double(bidPrice), C.int(bidVolume), C.double(askPrice), C.int(askVolume)))
}

/**
 * 发送报单
 *
 * 说明：
 * - 该接口是线程不安全的，不能在多线程调用同一个API实例对象的报单接口；
 * - 在 XTFSpi::onOrder() 接口中通知用户插入报单的结果和报单状态；
 *
 * @param inputOrder 待插入的报单参数
 * @param inputOrders 待批量插入的报单参数数组
 * @param orderCount 批量插入数量
 * @return 接口调用成功返回0，否则返回错误码
 *         调用成功并不表示报单操作的结果。
 *         ERRXTFAPI_InvalidImp
 *         ERRXTFAPI_NotStarted
 *         ERRXTFAPI_NotLoggedIn
 *         ERRXTFAPI_InvalidInstrument
 *         ERRXTFAPI_OrderCountExceeded
 *         以及网络发送可能出现的错误码
 */

// InsertOrder 委托
//
//	@receiver t
//	@param instrument 合约指针(需要用 t.GetInstrumentByID("合约代码") 获取)
//	@param order 委托参数
//	@return int 返回值:0 正确,其他 错误
func (t *Trade) InsertOrder(instrument unsafe.Pointer, order XTFInputOrder) int {
	return int(C.insertOrder(t.api, instrument, unsafe.Pointer(&order)))
}

func (t *Trade) CancelOrder(localID int32) int {
	return int(C.cancelOrder(t.api, C.long(localID)))
}

func (t *Trade) FindOrders() int {
	return int(C.findOrders(t.api))
}

//export exOnStart
func exOnStart(errorCode C.int, isFirstTime C._Bool) {
	if t.OnStart == nil {
		fmt.Println("exOnStart")
	} else {
		t.OnStart(int(errorCode), bool(isFirstTime))
	}
}

//export exOnStop
func exOnStop(errorCode C.int) {
	if t.OnStop == nil {
		fmt.Println("exOnStop")
	} else {
		t.OnStop(int(errorCode))
	}
}

//export exOnLogin
func exOnLogin(errorCode C.int, exchangeCount C.int) {
	if t.OnLogin == nil {
		fmt.Println("exOnLogin")
	} else {
		t.OnLogin(int(errorCode), int(exchangeCount))
	}
}

//export exOnLogout
func exOnLogout(errorCode C.int) {
	if t.OnLogout == nil {
		fmt.Println("exOnLogout")
	} else {
		t.OnLogout(int(errorCode))
	}
}

//export exOnChangePassword
func exOnChangePassword(errorCode C.int) {
	if t.OnChangePassword == nil {
		fmt.Println("exOnChangePassword")
	} else {
		t.OnChangePassword(int(errorCode))
	}
}

//export exOnReadyForTrading
func exOnReadyForTrading(account unsafe.Pointer) {
	if t.OnReadyForTrading == nil {
		fmt.Println("exOnReadyForTrading")
	} else {
		t.OnReadyForTrading((*XTFAccount)(unsafe.Pointer(account)))
	}
}

//export exOnLoadFinished
func exOnLoadFinished(account unsafe.Pointer) {
	if t.OnLoadFinished == nil {
		fmt.Println("exOnLoadFinished")
	} else {
		t.OnLoadFinished((*XTFAccount)(unsafe.Pointer(account)))
	}
}

//export exOnOrder
func exOnOrder(errorCode C.int, order unsafe.Pointer) {
	if t.OnOrder == nil {
		fmt.Println("exOnOrder")
	} else {
		t.OnOrder(int(errorCode), (*XTFOrder)(unsafe.Pointer(order)))
	}
}

//export exOnCancelOrder
func exOnCancelOrder(errorCode C.int, cancelOrder unsafe.Pointer) {
	if t.OnCancelOrder == nil {
		fmt.Println("exOnCancelOrder")
	} else {
		t.OnCancelOrder(int(errorCode), (*XTFOrder)(unsafe.Pointer(cancelOrder)))
	}
}

//export exOnTrade
func exOnTrade(trade unsafe.Pointer) {
	if t.OnTrade == nil {
		fmt.Println("exOnTrade")
	} else {
		t.OnTrade((*XTFTrade)(unsafe.Pointer(trade)))
	}
}

//export exOnAccount
func exOnAccount(event C.int, action C.int, account unsafe.Pointer) {
	if t.OnAccount == nil {
		fmt.Println("exOnAccount")
	} else {
		t.OnAccount(int(event), int(action), (*XTFAccount)(unsafe.Pointer(account)))
	}
}

//export exOnExchange
func exOnExchange(event C.int, channelID C.int, exchange unsafe.Pointer) {
	if t.OnExchange == nil {
		exc := (*XTFExchange)(unsafe.Pointer(exchange))
		fmt.Printf("exOnExchange: %s, event: %v, channelID: %v\n", exc.ExchangeID.String(), event, channelID)
	} else {
		t.OnExchange(int(event), int(channelID), (*XTFExchange)(unsafe.Pointer(exchange)))
	}
}

//export exOnInstrument
func exOnInstrument(event C.int, instrument unsafe.Pointer) {
	inst := (*XTFInstrument)(unsafe.Pointer(instrument))
	t.Instruments[inst.InstrumentID.String()] = instrument
	if t.OnInstrument != nil {
		t.OnInstrument(int(event), inst)
	}
}

//export exOnBookUpdate
func exOnBookUpdate(marketData unsafe.Pointer) {
	if t.OnBookUpdate == nil {
		fmt.Println("exOnBookUpdate")
	} else {
		t.OnBookUpdate((*XTFMarketData)(unsafe.Pointer(marketData)))
	}
}

//export exOnEvent
func exOnEvent(event unsafe.Pointer) {
	if t.OnEvent == nil {
		e := (*XTFEvent)(unsafe.Pointer(event))
		fmt.Printf("exOnEvent: time: %v, eventID: %v, eventType: %v\n", e.EventTime, e.EventID, e.EventType)
	} else {
		t.OnEvent((*XTFEvent)(unsafe.Pointer(event)))
	}
}

//export exOnError
func exOnError(errorCode C.int, data unsafe.Pointer, size C.ulong) {
	if t.OnError == nil {
		fmt.Printf("exOnError: id: %v\n", errorCode)
	} else {
		t.OnError(int(errorCode))
	}
}

// --------- 响应 -------------
func (t *Trade) SetOnStart() {
	C.SetOnStart(t.spi, C.exOnStart)
}

func (t *Trade) SetOnStop() {
	C.SetOnStop(t.spi, C.exOnStop)
}

func (t *Trade) SetOnLogin() {
	C.SetOnLogin(t.spi, C.exOnLogin)
}

func (t *Trade) SetOnLogout() {
	C.SetOnLogout(t.spi, C.exOnLogout)
}

func (t *Trade) SetOnChangePassword() {
	C.SetOnChangePassword(t.spi, C.exOnChangePassword)
}

func (t *Trade) SetOnReadyForTrading() {
	C.SetOnReadyForTrading(t.spi, C.exOnReadyForTrading)
}

func (t *Trade) SetOnLoadFinished() {
	C.SetOnLoadFinished(t.spi, C.exOnLoadFinished)
}

func (t *Trade) SetOrder() {
	C.SetOnOrder(t.spi, C.exOnOrder)
}

func (t *Trade) SetOnCancelOrder() {
	C.SetOnCancelOrder(t.spi, C.exOnCancelOrder)
}

func (t *Trade) SetOnTrade() {
	C.SetOnTrade(t.spi, C.exOnTrade)
}

func (t *Trade) SetOnAccount() {
	C.SetOnAccount(t.spi, C.exOnAccount)
}

func (t *Trade) SetOnExchange() {
	C.SetOnExchange(t.spi, C.exOnExchange)
}

func (t *Trade) SetOnInstrument() {
	C.SetOnInstrument(t.spi, C.exOnInstrument)
}

func (t *Trade) SetOnBookUpdate() {
	C.SetOnBookUpdate(t.spi, C.exOnBookUpdate)
}

func (t *Trade) SetOnEvent() {
	C.SetOnEvent(t.spi, C.exOnEvent)
}

func (t *Trade) SetOnError() {
	C.SetOnError(t.spi, C.exOnError)
}
